#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <windows.h>
#include <tchar.h>

#include <vector>
#include <iostream>
#include <cmath>

using namespace std;

typedef unsigned int IUINT32;

const int WIDTH = 800;
const int HEIGHT = 600;

const int RED = 0xff0000;
const int GREEN = 0x00ff00;
const int BLUE = 0x0000ff;

const int YELLOW = RED | GREEN;
const int PURPLE = GREEN | BLUE;
const int CYAN = RED | BLUE;

//=====================================================================
// 数学库
//=====================================================================
class Point2D {
	public:
		int x;
		int y;

		Point2D(int x, int y) : x(x), y(y) {}
};

template<typename T>
class Matrix {
	private:
		std::vector<std::vector<T>> data;
		int rows;
		int cols;

	public:
		// 构造函数：指定行数和列数，初始化为0
		Matrix(int r, int c) : rows(r), cols(c), data(r, std::vector<T>(c, 0)) {}

		// 构造函数：使用二维 vector 初始化
		Matrix(const std::vector<std::vector<float>>& matrix)
			: data(matrix), rows(matrix.size()), cols(matrix[0].size()) {}

		// 获取行数
		int numRows() const {
			return rows;
		}

		// 获取列数
		int numCols() const {
			return cols;
		}

		// 访问元素（非修改）
		const std::vector<T>& operator[](int i) const {
			if (i < 0 || i >= rows)
				throw std::out_of_range("Row index out of range");
			return data[i];
		}

		// 访问元素（允许修改）
		std::vector<T>& operator[](int i) {
			if (i < 0 || i >= rows)
				throw std::out_of_range("Row index out of range");
			return data[i];
		}

		// 矩阵加法
		Matrix operator+(const Matrix& other) const {
			if (rows != other.rows || cols != other.cols)
				throw std::invalid_argument("Matrices must have the same dimensions for addition");

			Matrix result(rows, cols);
			for (int i = 0; i < rows; ++i)
				for (int j = 0; j < cols; ++j)
					result[i][j] = data[i][j] + other.data[i][j];
			return result;
		}

		// 矩阵减法
		Matrix operator-(const Matrix& other) const {
			if (rows != other.rows || cols != other.cols)
				throw std::invalid_argument("Matrices must have the same dimensions for subtraction");

			Matrix result(rows, cols);
			for (int i = 0; i < rows; ++i)
				for (int j = 0; j < cols; ++j)
					result[i][j] = data[i][j] - other.data[i][j];
			return result;
		}

		// 矩阵乘法
		Matrix operator*(const Matrix& other) const {
			if (cols != other.rows)
				throw std::invalid_argument("Number of columns in first matrix must match number of rows in second matrix");

			Matrix result(rows, other.cols);
			for (int i = 0; i < rows; ++i)
				for (int j = 0; j < other.cols; ++j) {
					result[i][j] = 0;
					for (int k = 0; k < cols; ++k)
						result[i][j] += data[i][k] * other.data[k][j];
				}
			return result;
		}

		// 打印矩阵
		void print() const {
			for (int i = 0; i < rows; ++i) {
				for (int j = 0; j < cols; ++j) {
					std::cout << data[i][j] << " ";
				}
				std::cout << std::endl;
			}
		}

		// 静态函数，用于创建转换矩阵
		static Matrix<float> get_identity_matrix() {
			Matrix<float> m({
				{1.0, 0.0, 0.0, 0.0},
				{0.0, 1.0, 0.0, 0.0},
				{0.0, 0.0, 1.0, 0.0},
				{0.0, 0.0, 0.0, 1.0}
			});

			return m;
		}

		static Matrix<float> translate(float x, float y, float z) {
			Matrix<float> m = get_identity_matrix();
			m[0][3] = x;
			m[1][3] = y;
			m[2][3] = z;
			return m;
		}

		static Matrix<float> scale(float x, float y, float z) {
			Matrix<float> m = get_identity_matrix();
			m[0][0] = x;
			m[1][1] = y;
			m[2][2] = z;
			return m;
		}

		static Matrix<float> rotate(float x, float y, float z, float theta);
};

class Vec4 {
	public:
		float x;
		float y;
		float z;
		float w;

		Vec4(float x, float y, float z) : x(x), y(y), z(z), w(1.0) {}
		Vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}

		// 向量加法
		Vec4 operator+(const Vec4& v) const {
			return Vec4(x + v.x, y + v.y, z + v.z, w + v.w);
		}

		// 向量减法
		Vec4 operator-(const Vec4& v) const {
			return Vec4(x - v.x, y - v.y, z - v.z, w - v.w);
		}

		// 标量乘法（向量 * 标量）
		Vec4 operator*(float scalar) const {
			return Vec4(x * scalar, y * scalar, z * scalar, w * scalar);
		}

		// 标量乘法（标量 * 向量）——友元函数重载
		friend Vec4 operator*(float scalar, const Vec4& v) {
			return Vec4(v.x * scalar, v.y * scalar, v.z * scalar, v.w * scalar);
		}

		// 点积（Dot Product）
		float dot(const Vec4& v) const {
			return x * v.x + y * v.y + z * v.z + w * v.w;
		}

		// 叉积 (Cross Product)
		Vec4 cross_product(const Vec4& v) const {
			return Vec4(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x, 1.0);
		}

		// 向量长度（模）
		float length() const {
			return std::sqrt(x * x + y * y + z * z + w * w);
		}

		// 单位化（归一化）
		Vec4 normalize() const {
			float len = length();
			if (len == 0.0f) return Vec4(0.0f, 0.0f, 0.0f, 0.0f); // 防除零
			return Vec4(x / len, y / len, z / len, w / len);
		}

		// 打印向量
		void print() const {
			std::cout << "Vec4(" << x << ", " << y << ", " << z << ", " << w << ")" << std::endl;
		}

		Vec4 apply_transform(Matrix<float> t) {
			return Vec4(
			           x * t[0][0] + y * t[0][1] + z * t[0][2] + w * t[0][3],
			           x * t[1][0] + y * t[1][1] + z * t[1][2] + w * t[1][3],
			           x * t[2][0] + y * t[2][1] + z * t[2][2] + w * t[2][3],
			           x * t[3][0] + y * t[3][1] + z * t[3][2] + w * t[3][3]
			       );
		}
};

template<typename T>
Matrix<float> Matrix<T>::rotate(float x, float y, float z, float theta) {
	float qsin = (float)sin(theta * 0.5f);
	float qcos = (float)cos(theta * 0.5f);
	Vec4 vec = { x, y, z, 1.0f };
	float w = qcos;
	vec.normalize();
	x = vec.x * qsin;
	y = vec.y * qsin;
	z = vec.z * qsin;
	Matrix<float> m = get_identity_matrix();
	m[0][0] = 1 - 2 * y * y - 2 * z * z;
	m[1][0] = 2 * x * y - 2 * w * z;
	m[2][0] = 2 * x * z + 2 * w * y;
	m[0][1] = 2 * x * y + 2 * w * z;
	m[1][1] = 1 - 2 * x * x - 2 * z * z;
	m[2][1] = 2 * y * z - 2 * w * x;
	m[0][2] = 2 * x * z - 2 * w * y;
	m[1][2] = 2 * y * z + 2 * w * x;
	m[2][2] = 1 - 2 * x * x - 2 * y * y;
	m[0][3] = m[1][3] = m[2][3] = 0.0f;
	m[3][0] = m[3][1] = m[3][2] = 0.0f;
	m[3][3] = 1.0f;

	return m;
}

class PointShaded {
	public:
		Vec4 pos;

		float light;

		PointShaded(Vec4 pos, float light) : pos(pos), light(light) {}
};

int shade_color(int color, float h) {
	int R = (color >> 16) & 0xff;
	int G = (color >> 8) & 0xff;
	int B = color & 0xff;

	color = (((int)(R * h)) << 16) | (((int)(G * h)) << 8) | (int)(B * h);
	return color;
}



// 定义三角形
class Triangle {
	public:
		vector<int> vertex_indices;
		int color;
};


// 定义模型，使用顶点和三角形进行定义
class Model {
	public:
		vector<Vec4> vertices;
		vector<Triangle> triangles;

		Model(vector<Vec4> vs, vector<Triangle> ts) : vertices(vs), triangles(ts) {}
};

const Model cube = {
	{
		{1, 1, 1},
		{-1, 1, 1},
		{-1, -1, 1},
		{1, -1, 1},
		{1, 1, -1},
		{-1, 1, -1},
		{-1, -1, -1},
		{1, -1, -1},
	},
	{
		
		
		{{0, 1, 2}, RED},
		{{0, 2, 3}, RED},
		{{4, 0, 3}, GREEN},
		{{4, 3, 7}, GREEN},
		{{5, 4, 7}, BLUE},
		{{5, 7, 6}, BLUE},
		{{1, 5, 6}, YELLOW},
		{{1, 6, 2}, YELLOW},
		{{4, 5, 1}, PURPLE},
		{{4, 1, 0}, PURPLE},
		{{2, 6, 7}, CYAN},
		{{2, 7, 3}, CYAN},
	}
};

class Instance {
	public:
		const Model& m;

		Vec4 position;
		Vec4 scale;
		Vec4 rotate;

		Instance(const Model& m, Vec4 v1, Vec4 v2, Vec4 v3) : m(m), position(v1), scale(v2), rotate(v3) {}
};


//=====================================================================
// 模型转换
//=====================================================================
float view_port_d = 3;
float Vw = 4;
float Vh = 3;

Point2D viewport_to_canvas(float x, float y) {
	return Point2D(x / Vw * WIDTH + WIDTH / 2, y / Vh * HEIGHT + HEIGHT / 2);
}

Vec4 project_vertex(Vec4 p) {
	Point2D p2d = viewport_to_canvas(p.x * view_port_d / p.z, p.y * view_port_d / p.z);
	return {(float)p2d.x, (float)p2d.y, 1 / p.z, 1.0};
}


//=====================================================================
// 定义光照
//=====================================================================
enum LightType {
    LT_POINT,
    LT_AMBIENT,
    LT_DIRECTIONAL
};

class Light {
public:
    LightType light_type;
    float intensity;
    Vec4 position;

public:
    Light(LightType t, float it, Vec4 pos) : light_type(t),
        intensity(it), position(pos) {}
};

Light lights[] = {
    {
        LightType::LT_AMBIENT,
        0.2,
        Vec4(0, 0, 0)
    },
    {
        LightType::LT_POINT,
        0.6,
        Vec4(2, 1, 0)
    }, 
    {
        LightType::LT_DIRECTIONAL,
        0.2,
        Vec4(1, 4, 4)
    }
};

const int LIGHT_NUM = sizeof(lights) / sizeof(Light);


float computeLighting(const Vec4 &point, const Vec4 &N,
                      Vec4 &&V, float specular)
{
    float li = 0.0;
    for (int i = 0; i < LIGHT_NUM; i++)
    {
        if (lights[i].light_type == LightType::LT_AMBIENT)
        {
            li += lights[i].intensity;
        }
        else
        {
            Vec4 light_vec(0, 0, 0);

            if (lights[i].light_type == LightType::LT_DIRECTIONAL)
            {
                light_vec = lights[i].position; // 这里实际上是平行光的方向
            }
            else if (lights[i].light_type == LightType::LT_POINT)
            {
                light_vec = lights[i].position - point;
            }

            float n_dot_l = N.dot(light_vec);
            if (n_dot_l > 0)
            {
                // 因为 N 是单位法向量，这里就不再除以 N 的长度了。
                li += lights[i].intensity * n_dot_l / light_vec.length();
            }

            if (specular > 0)
            {
                Vec4 R = N * (2 * N.dot(light_vec)) - light_vec;
                float r_dot_v = R.dot(V);
                if (r_dot_v > 0)
                {
                    li += lights[i].intensity * pow(r_dot_v / (R.length() * V.length()),
                                                    specular);
                }
            }
        }
    }

    if (li > 1.0)
    {
        li = 1.0;
    }

    return li;
}



//=====================================================================
// 渲染设备
//=====================================================================
typedef struct {
	//transform_t transform;      // 坐标变换器
	int width;                  // 窗口宽度
	int height;                 // 窗口高度
	IUINT32 **framebuffer;      // 像素缓存：framebuffer[y] 代表第 y行
	float **zbuffer;            // 深度缓存：zbuffer[y] 为第 y行指针
	IUINT32 **texture;          // 纹理：同样是每行索引
	int tex_width;              // 纹理宽度
	int tex_height;             // 纹理高度
	float max_u;                // 纹理最大宽度：tex_width - 1
	float max_v;                // 纹理最大高度：tex_height - 1
	int render_state;           // 渲染状态
	IUINT32 background;         // 背景颜色
	IUINT32 foreground;         // 线框颜色
}	device_t;

#define RENDER_STATE_WIREFRAME      1		// 渲染线框
#define RENDER_STATE_TEXTURE        2		// 渲染纹理
#define RENDER_STATE_COLOR          4		// 渲染颜色

// 设备初始化，fb为外部帧缓存，非 NULL 将引用外部帧缓存（每行 4字节对齐）
void device_init(device_t *device, int width, int height, void *fb) {
	char *framebuf, *zbuf;
	int j;
	device->framebuffer = (IUINT32**)malloc(sizeof(void*) * height);
	device->zbuffer = (float**)malloc(sizeof(void*) * height);
	device->texture = (IUINT32**)malloc(sizeof(void*) * 1024);
	zbuf = (char*)malloc(width * height * sizeof(float));

	if (fb != NULL) {
		framebuf = (char*)fb;
	} else {
		framebuf = (char*)malloc(width * height * 4);
	}

	for (j = 0; j < height; j++) {
		device->framebuffer[j] = (IUINT32*)(framebuf + width * 4 * j);
		device->zbuffer[j] = (float*)(zbuf + width * sizeof(float) * j);
	}
	//device->texture[0] = (IUINT32*)ptr;
	//device->texture[1] = (IUINT32*)(ptr + 16);
	//memset(device->texture[0], 0, 64);
	//device->tex_width = 2;
	//device->tex_height = 2;
	device->max_u = 1.0f;
	device->max_v = 1.0f;
	device->width = width;
	device->height = height;
	device->background = 0xc0c0c0;
	device->foreground = 0;
	//transform_init(&device->transform, width, height);
	device->render_state = RENDER_STATE_WIREFRAME;
}

// 清空 framebuffer 和 zbuffer
void device_clear(device_t *device, int mode) {
	int y, x, height = device->height;

	for (y = 0; y < device->height; y++) {
		IUINT32 *dst = device->framebuffer[y];
		IUINT32 cc = (height - 1 - y) * 230 / (height - 1);
		cc = (cc << 16) | (cc << 8) | cc;
		if (mode == 0) cc = device->background;
		for (x = device->width; x > 0; dst++, x--) dst[0] = cc;
	}
	for (y = 0; y < device->height; y++) {
		float *dst = device->zbuffer[y];
		for (x = device->width; x > 0; dst++, x--) dst[0] = 0;
	}
}

// 画点
void device_pixel(device_t *device, int x, int y, IUINT32 color) {
	if (((IUINT32)x) < (IUINT32)device->width && ((IUINT32)y) < (IUINT32)device->height) {
		device->framebuffer[y][x] = color;
	}
}

template<typename T>
vector<T> interpolate(int x0, T y0, int x1, T y1) {
	vector<T> vec;
	int dx = x1 - x0;
	T dy = y1 - y0;
	float a = (float)dy / (float) dx;

	int x = x0;
	float y = y0;

	for (; x <= x1; x += 1) {
		vec.push_back((T)y);
		y += a;
	}

	return vec;
}

template<typename T>
T linear_interp(int x0, int x1, T v0, T v1, int x) {
	if (x0 == x1)
		return v0;

	return x * (v1 - v0) / (x1 - x0);
}

// 绘制线段
void device_draw_line(device_t *device, Point2D p0, Point2D p1, IUINT32 c) {
	int x, y;
	int x1 = p0.x;
	int y1 = p0.y;
	int x2 = p1.x;
	int y2 = p1.y;

	if (x1 == x2 && y1 == y2) {
		device_pixel(device, x1, y1, c);
	}	else if (x1 == x2) {
		int inc = (y1 <= y2) ? 1 : -1;
		for (y = y1; y != y2; y += inc) device_pixel(device, x1, y, c);
		device_pixel(device, x2, y2, c);
	}	else if (y1 == y2) {
		int inc = (x1 <= x2) ? 1 : -1;
		for (x = x1; x != x2; x += inc) device_pixel(device, x, y1, c);
		device_pixel(device, x2, y2, c);
	}	else {
		int dx = x2 - x1;
		int dy = y2 - y1;

		if (abs(dx) >= abs(dy)) {
			if (x1 > x2) {
				int x = x1;
				x1 = x2, x2 = x;
				int y = y1;
				y1 = y2, y2 = y;
			}

			vector<int> ys = interpolate<int>(x1, y1, x2, y2);

			for (int x = x1, i = 0; x <= x2; x++, i++) {
				device_pixel(device, x, ys[i], c);
			}
		} else {
			if (y1 > y2) {
				int x = x1;
				x1 = x2, x2 = x;
				int y = y1;
				y1 = y2, y2 = y;
			}

			vector<int> xs = interpolate<int>(y1, x1, y2, x2);

			for (int y = y1, i = 0; y <= y2; y++, i++) {
				device_pixel(device, xs[i], y, c);
			}
		}
	}
}


// 绘制三角形
void device_draw_triangle(device_t *device, Point2D p0, Point2D p1, Point2D p2, IUINT32 c) {
	device_draw_line(device, p0, p1, c);
	device_draw_line(device, p1, p2, c);
	device_draw_line(device, p0, p2, c);
}

// 绘制填充三角形
void device_filled_triangle(device_t *device, Vec4 p0, Vec4 p1, Vec4 p2, IUINT32 c) {
	if (p1.y < p0.y) {
		swap<Vec4>(p0, p1);
	}
	if (p2.y < p0.y) {
		swap<Vec4>(p0, p2);
	}
	if (p2.y < p1.y) {
		swap<Vec4>(p1, p2);
	}

	vector<int> x01 = interpolate<int>(p0.y, p0.x, p1.y, p1.x);
	vector<int> x02 = interpolate<int>(p0.y, p0.x, p2.y, p2.x);
	vector<int> x12 = interpolate<int>(p1.y, p1.x, p2.y, p2.x);

	x01.pop_back();
	x01.insert(x01.end(), x12.begin(), x12.end());

	vector<float> z01 = interpolate<float>(p0.y, p0.z, p1.y, p1.z);
	vector<float> z02 = interpolate<float>(p0.y, p0.z, p2.y, p2.z);
	vector<float> z12 = interpolate<float>(p1.y, p1.z, p2.y, p2.z);

	z01.pop_back();
	z01.insert(z01.end(), z12.begin(), z12.end());

	for (int y = p0.y; y <= p2.y; y++) {
		int xb = x01[y - p0.y];
		int xe = x02[y - p0.y];

		float zb = z01[y - p0.y];
		float ze = z02[y - p0.y];

		int step = 1;
		if (xb > xe) step = -1;
		float k = (ze - zb) / (xe - xb);

		for (int x = xb; x != xe; x += step) {
			float z = zb + k * (x - xb);
			
			if (device->zbuffer[y][x] < z) {
				device->zbuffer[y][x] = z;
				device_pixel(device, x, y, c);
			}
		}
	}

	/*
	device_draw_line(device, {p0.x, p0.y}, {p1.x, p1.y}, 0x0000ff);
	device_draw_line(device, {p1.x, p1.y}, {p2.x, p2.y}, 0xff);
	device_draw_line(device, {p0.x, p0.y}, {p2.x, p2.y}, 0xff);
	*/
}

// 绘制阴影三角形
void device_shaded_triangle(device_t *device, PointShaded p0, PointShaded p1, PointShaded p2, IUINT32 c) {
	if (p1.pos.y < p0.pos.y) {
		swap<PointShaded>(p0, p1);
	}
	if (p2.pos.y < p0.pos.y) {
		swap<PointShaded>(p0, p2);
	}
	if (p2.pos.y < p1.pos.y) {
		swap<PointShaded>(p1, p2);
	}
	
	int p0x = (int)p0.pos.x;
	int p0y = (int)p0.pos.y;
	int p1x = (int)p1.pos.x;
	int p1y = (int)p1.pos.y;
	int p2x = (int)p2.pos.x;
	int p2y = (int)p2.pos.y;

	vector<int> x01 = interpolate(p0y, p0x, p1y, p1x);
	vector<int> x02 = interpolate(p0y, p0x, p2y, p2x);
	vector<int> x12 = interpolate(p1y, p1x, p2y, p2x);

	vector<float> h01 = interpolate(p0y, p0.light, p1y, p1.light);
	vector<float> h02 = interpolate(p0y, p0.light, p2y, p2.light);
	vector<float> h12 = interpolate(p1y, p1.light, p2y, p2.light);

	x01.pop_back();
	x01.insert(x01.end(), x12.begin(), x12.end());

	h01.pop_back();
	h01.insert(h01.end(), h12.begin(), h12.end());
	
	vector<float> z01 = interpolate<float>(p0y, p0.pos.z, p1y, p1.pos.z);
	vector<float> z02 = interpolate<float>(p0y, p0.pos.z, p2y, p2.pos.z);
	vector<float> z12 = interpolate<float>(p1y, p1.pos.z, p2y, p2.pos.z);
	
	z01.pop_back();
	z01.insert(z01.end(), z12.begin(), z12.end());

	for (int y = p0y; y <= p2y; y++) {
		int xb = x01[y - p0y];
		int xe = x02[y - p0y];

		float hb = h01[y - p0y];
		float he = h02[y - p0y];
		
		float zb = z01[y - p0y];
		float ze = z02[y - p0y];

		int step = 1;
		if (xb > xe) step = -1;
		float hk = (he - hb) / (xe - xb);
		float zk = (ze - zb) / (xe - xb);

		for (int x = xb; x != xe; x += step) {
			float h = hb + hk * (x - xb);
			float z = zb + zk * (x - xb);
						
			if (device->zbuffer[y][x] < z) {
				device->zbuffer[y][x] = z;
				device_pixel(device, x, y, shade_color(c, h));
			}
		}
	}
}

// 以 Model 和 Transform 矩阵的形式计算模型的顶点坐标
void device_render_model(device_t *device, const Instance& inst) {
	vector<Vec4> projected;
	vector<Vec4> transformed;

	Vec4 trans = inst.position;
	Matrix<float> t = Matrix<float>::translate(trans.x, trans.y, trans.z);
	Vec4 scale = inst.scale;
	Matrix<float> s = Matrix<float>::scale(scale.x, scale.y, scale.z);
	Vec4 rotate = inst.rotate;
	static float theta = rotate.w;
	Matrix<float> r = Matrix<float>::rotate(rotate.x, rotate.y, rotate.z, theta);
	theta += 0.01;

	Matrix<float> world = t * r * s;

	for (auto V = inst.m.vertices.begin(); V != inst.m.vertices.end(); V++) {
		Vec4 p = *V;
		p = p.apply_transform(world);
		transformed.push_back(p);
		projected.push_back(project_vertex(p));
	}

	for (auto T = inst.m.triangles.begin(); T != inst.m.triangles.end(); T++) {
		int a = (*T).vertex_indices[0];
		int b = (*T).vertex_indices[1];
		int c = (*T).vertex_indices[2];

		// 背面剔除
		Vec4 A = transformed[a];
		Vec4 B = transformed[b];
		Vec4 C = transformed[c];

		Vec4 N = (C - A).cross_product(B - A);
		if (N.z < 0)
			continue;

		//device_draw_triangle(device,
		//device_filled_triangle(device,
		//                       projected[a], projected[b], projected[c], (*T).color);
		
		device_shaded_triangle(device, 
			PointShaded(projected[a], 0.5), 
			PointShaded(projected[b], 1.0), 
			PointShaded(projected[c], 0.0), (*T).color);
	}
}


//=====================================================================
// Win32 窗口及图形绘制：为 device 提供一个 DibSection 的 FB
//=====================================================================
int screen_w, screen_h, screen_exit = 0;
int screen_mx = 0, screen_my = 0, screen_mb = 0;
int screen_keys[512];	// 当前键盘按下状态
static HWND screen_handle = NULL;		// 主窗口 HWND
static HDC screen_dc = NULL;			// 配套的 HDC
static HBITMAP screen_hb = NULL;		// DIB
static HBITMAP screen_ob = NULL;		// 老的 BITMAP
unsigned char *screen_fb = NULL;		// frame buffer
long screen_pitch = 0;

int screen_init(int w, int h, const TCHAR *title);	// 屏幕初始化
int screen_close(void);								// 关闭屏幕
void screen_dispatch(void);							// 处理消息
void screen_update(void);							// 显示 FrameBuffer

static LRESULT screen_events(HWND, UINT, WPARAM, LPARAM);  // 窗口事件处理

#ifdef _MSC_VER
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")
#endif

// 初始化窗口并设置标题
int screen_init(int w, int h, const TCHAR *title) {
	WNDCLASS wc = { CS_BYTEALIGNCLIENT, (WNDPROC)screen_events, 0, 0, 0,
	                NULL, NULL, NULL, NULL, _T("TINY3D")
	              };

	//位图高度（像素），如果为负值表示图像数据是自上而下的
	BITMAPINFO bi = { {
			sizeof(BITMAPINFOHEADER), w, -h, 1, 32, BI_RGB,
			w * h * 4, 0, 0, 0, 0
		}
	};
	RECT rect = { 0, 0, w, h };
	int wx, wy, sx, sy;
	LPVOID ptr;
	HDC hDC;

	screen_close();

	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.hInstance = GetModuleHandle(NULL);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	if (!RegisterClass(&wc)) return -1;

	screen_handle = CreateWindow(_T("TINY3D"), title,
	                             WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
	                             0, 0, 0, 0, NULL, NULL, wc.hInstance, NULL);
	if (screen_handle == NULL) return -2;

	screen_exit = 0;
	hDC = GetDC(screen_handle);
	screen_dc = CreateCompatibleDC(hDC);
	ReleaseDC(screen_handle, hDC);

	screen_hb = CreateDIBSection(screen_dc, &bi, DIB_RGB_COLORS, &ptr, 0, 0);
	if (screen_hb == NULL) return -3;

	screen_ob = (HBITMAP)SelectObject(screen_dc, screen_hb);
	screen_fb = (unsigned char*)ptr;
	screen_w = w;
	screen_h = h;
	screen_pitch = w * 4;

	AdjustWindowRect(&rect, GetWindowLong(screen_handle, GWL_STYLE), 0);
	wx = rect.right - rect.left;
	wy = rect.bottom - rect.top;
	sx = (GetSystemMetrics(SM_CXSCREEN) - wx) / 2;
	sy = (GetSystemMetrics(SM_CYSCREEN) - wy) / 2;
	if (sy < 0) sy = 0;
	SetWindowPos(screen_handle, NULL, sx, sy, wx, wy, (SWP_NOCOPYBITS | SWP_NOZORDER | SWP_SHOWWINDOW));
	SetForegroundWindow(screen_handle);

	ShowWindow(screen_handle, SW_NORMAL);
	screen_dispatch();

	memset(screen_keys, 0, sizeof(int) * 512);
	memset(screen_fb, 0, w * h * 4);

	return 0;
}

int screen_close(void) {
	if (screen_dc) {
		if (screen_ob) {
			SelectObject(screen_dc, screen_ob);
			screen_ob = NULL;
		}
		DeleteDC(screen_dc);
		screen_dc = NULL;
	}
	if (screen_hb) {
		DeleteObject(screen_hb);
		screen_hb = NULL;
	}
	if (screen_handle) {
		CloseWindow(screen_handle);
		screen_handle = NULL;
	}
	return 0;
}

static LRESULT screen_events(HWND hWnd, UINT msg,
                             WPARAM wParam, LPARAM lParam) {
	switch (msg) {
		case WM_CLOSE:
			screen_exit = 1;
			break;
		case WM_KEYDOWN:
			screen_keys[wParam & 511] = 1;
			break;
		case WM_KEYUP:
			screen_keys[wParam & 511] = 0;
			break;
		default:
			return DefWindowProc(hWnd, msg, wParam, lParam);
	}
	return 0;
}

void screen_dispatch(void) {
	MSG msg;
	while (1) {
		if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) break;
		if (!GetMessage(&msg, NULL, 0, 0)) break;
		DispatchMessage(&msg);
	}
}

void screen_update(void) {
	HDC hDC = GetDC(screen_handle);
	BitBlt(hDC, 0, 0, screen_w, screen_h, screen_dc, 0, 0, SRCCOPY);
	ReleaseDC(screen_handle, hDC);
	screen_dispatch();
}


int main(void) {
	device_t device;
	int states[] = { RENDER_STATE_TEXTURE, RENDER_STATE_COLOR, RENDER_STATE_WIREFRAME };
	int indicator = 0;
	int kbhit = 0;

	TCHAR *title = _T("Mini3d (software render tutorial) - ")
	               _T("Left/Right: rotation, Up/Down: forward/backward, Space: switch state");

	if (screen_init(WIDTH, HEIGHT, title))
		return -1;

	device_init(&device, 800, 600, screen_fb);

	while (screen_exit == 0 && screen_keys[VK_ESCAPE] == 0) {
		screen_dispatch();
		device_clear(&device, 1);

		if (screen_keys[VK_SPACE]) {
			if (kbhit == 0) {
				kbhit = 1;
				if (++indicator >= 3) indicator = 0;
				device.render_state = states[indicator];
			}
		}	else {
			kbhit = 0;
		}

		//device_draw_line(&device, Point2D(150, 100), Point2D(100, 150), 0x0000ff);
		//device_filled_triangle(&device, Point2D(100, 100), Point2D(500, 300), Point2D(300, 500), 0xff0000);
		//device_shaded_triangle(&device, PointShaded(100, 100, 1.0), PointShaded(500, 300, 1.0), PointShaded(300, 500, 1.0), 0x00ff00);
		//device_draw_cube(&device);

		const Instance inst = {
			cube,
			{1, 2, 7, 0.0},
			{1.0, 0.5, 1.0, 0.0},
			{0.0, 1.0, 0.0, 0.78}
		};

		device_render_model(&device, inst);

		screen_update();
		Sleep(1);
	}
	return 0;
}
